package com.xxx.oauth.server.controller;

import java.net.URI;
import java.net.URISyntaxException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.oltu.oauth2.common.message.types.ResponseType;
import org.apache.oltu.oauth2.common.utils.OAuthUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;

import com.xxx.oauth.server.constant.Constants;
import com.xxx.oauth.server.service.ClientService;
import com.xxx.oauth.server.service.OAuthService;

/**
 * 授权码请求处理 Controller（授权服务器角色）
 */
@Controller
public class AuthorizeController {

    @Autowired
    private OAuthService oAuthService;
    
    @Autowired
    private ClientService clientService;

    /**
     * 1.请求授权码
     * @param model
     * @param request
     * @return
     * @throws URISyntaxException
     * @throws OAuthSystemException
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
	@RequestMapping("/authorize")
    public Object authorize(Model model, HttpServletRequest request) throws URISyntaxException, OAuthSystemException {
        try {
            //构建OAuth授权请求
            OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request);

            //1.检查传入的客户端id是否正确（clientId是oauth-server分配给oauth-client的）
            if (!oAuthService.checkClientId(oauthRequest.getClientId())) {
                OAuthResponse response = OAuthASResponse
                				.errorResponse(HttpServletResponse.SC_BAD_REQUEST) // 400
                                .setError(OAuthError.TokenResponse.INVALID_CLIENT)
                                .setErrorDescription(Constants.INVALID_CLIENT_DESCRIPTION)
                                .buildJSONMessage();
                return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
            }

            //2.获取登录用户名
            Subject subject = SecurityUtils.getSubject(); //只要登录成功，通过这个就能拿到登录用户username
            if(!subject.isAuthenticated()) { // 未认证
                if(!login(subject, request)) {// 进行登录，如果失败时跳转到登陆页面
                    model.addAttribute("client", clientService.findByClientId(oauthRequest.getClientId()));
                    return "oauth2login";
                }
            }

            //3.用户已登录，生成授权码、并将授权码和用户名存放到cache里
            String username = (String)subject.getPrincipal(); // 用户名
            String authorizationCode = null;
            //responseType目前仅支持CODE，另外还有TOKEN
            String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);
            if (responseType.equals(ResponseType.CODE.toString())) {
                OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
                authorizationCode = oauthIssuerImpl.authorizationCode(); // 授权码
                // 将授权码和username存放到cache里，key为授权码，value为username
                oAuthService.addAuthCode(authorizationCode, username);
            }

            //4.构建 OAuth-授权 响应
            OAuthASResponse.OAuthAuthorizationResponseBuilder builder =
                    OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_FOUND);
            builder.setCode(authorizationCode); //设置授权码
            String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI); //客户端重定向地址

            //5.构建OAuth响应
            final OAuthResponse response = builder.location(redirectURI).buildQueryMessage();
            HttpHeaders headers = new HttpHeaders();
            headers.setLocation(new URI(response.getLocationUri()));
            return new ResponseEntity(headers, HttpStatus.valueOf(response.getResponseStatus()));
        } catch (OAuthProblemException e) { // 异常处理
        	System.err.println(e);
            String redirectUri = e.getRedirectUri();
            if (OAuthUtils.isEmpty(redirectUri)) { // redirectUri为必填项
                //客户端没有传入redirectUri直接报错
                return new ResponseEntity("OAuth callback url needs to be provided by client!!!", 
                		HttpStatus.NOT_FOUND);
            }

            //返回错误消息
            final OAuthResponse response = OAuthASResponse
            				.errorResponse(HttpServletResponse.SC_FOUND) // 302
                            .error(e)
                            .location(redirectUri)
                            .buildQueryMessage();
            HttpHeaders headers = new HttpHeaders();
            headers.setLocation(new URI(response.getLocationUri()));
            return new ResponseEntity(headers, HttpStatus.valueOf(response.getResponseStatus()));
        }
    }

    /**
     * 登录处理
     * @param subject
     * @param request
     * @return
     */
    private boolean login(Subject subject, HttpServletRequest request) {
        if("get".equalsIgnoreCase(request.getMethod())) { // 登录必须使用post方式
            return false;
        }
        
        //前台请求过来的username和password
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
            return false;
        }

        // 构造登录用户名/密码token，准备进行登录
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            subject.login(token); // 登录，如果没有抛出异常，说明登录成功
            return true;
        } catch (Exception e) {
            request.setAttribute("error", "登录失败:" + e.getClass().getName());
            return false;
        }
    }
}